//! 错误码定义与生成模块
//!
//! los_error.c/h的rust直译
//!
//! # 待办
//!
//! 1. 编译链接参数未实现
//! 2. 错误处理函数原型的void*参数暂以字节切片代替
//! 3. rust没有任意标号goto，因此去掉OS_GOTO_ERR_HANDLER
//! 4. 代码样式编译报警很多

// 以下宏仅用于测试
#![allow(non_camel_case_types)]
#![allow(dead_code)]
#![allow(non_snake_case)]
#![allow(non_upper_case_globals)]

/**
  * @ingroup los_errno
  * OS error code flag.
  */
const LOS_ERRNO_OS_ID: u32 = 0x00 << 16;

/**
  * @ingroup los_errno
  * Define the error level as informative.
  */
const LOS_ERRTYPE_NORMAL: u32 = 0x00 << 24;

/**
  * @ingroup los_errno
  * Define the error level as warning.
  */
const LOS_ERRTYPE_WARN: u32 = 0x01 << 24;

/**
  * @ingroup los_errno
  * Define the error level as critical.
  */
const LOS_ERRTYPE_ERROR: u32 = 0x02 << 24;

/**
  * @ingroup los_errno
  * Define the error level as fatal.
  */
const LOS_ERRTYPE_FATAL: u32 = 0x03 << 24;

/**
  * @ingroup los_errno
  * Define fatal OS errors.
  *
  * 首个参数只接受LosModuleId::类标识符，第二个参数只接受数字，否则编译报错
  * C名称：LOS_ERRNO_OS_FATAL
  */
#[macro_export]
macro_rules! los_errno_os_fatal {
  ($mod_id: path, $errno: literal) => {
    {
      let mod_id: LosModuleId = $mod_id;
      LOS_ERRTYPE_FATAL | LOS_ERRNO_OS_ID | (mod_id as u32) << 8 |  $errno
    }
  };
}

/**
  * @ingroup los_errno
  * Define critical OS errors.
  *
  * 首个参数只接受LosModuleId::类标识符，第二个参数只接受数字，否则编译报错
  * C名称：LOS_ERRNO_OS_ERROR
  */
#[macro_export]
macro_rules! los_errno_os_error {
  ($mod_id: path, $errno: literal) => {
    {
      let mod_id: LosModuleId = $mod_id;
      LOS_ERRTYPE_ERROR | LOS_ERRNO_OS_ID | (mod_id as u32) << 8 |  $errno
    }
  };
}

/**
  * @ingroup los_errno
  * Define warning OS errors.
  *
  * 首个参数只接受LosModuleId::类标识符，第二个参数只接受数字，否则编译报错
  * C名称：LOS_ERRNO_OS_WARN
  */
#[macro_export]
macro_rules! los_errno_os_warn {
  ($mod_id: path, $errno: literal) => {
    {
      let mod_id: LosModuleId = $mod_id;
      LOS_ERRTYPE_WARN | LOS_ERRNO_OS_ID | (mod_id as u32) << 8 |  $errno
    }
  };
}

/**
  * @ingroup los_errno
  * Define informative OS errors.
  *
  * 首个参数只接受LosModuleId::类标识符，第二个参数只接受数字，否则编译报错
  * C名称：LOS_ERRNO_OS_NORMAL
  */
#[macro_export]
macro_rules! los_errno_os_normal {
  ($mod_id: path, $errno: literal) => {
    {
      let mod_id: LosModuleId = $mod_id;
      LOS_ERRTYPE_NORMAL | LOS_ERRNO_OS_ID | (mod_id as u32) << 8 |  $errno
    }
  };
}

/**
 * @ingroup los_err
 * @brief Define the pointer to the error handling function.
 *
 * @par Description:
 * This API is used to define the pointer to the error handling function.
 * @attention
 * <ul>
 * <li>None.</li>
 * </ul>
 *
 * @param  fileName  [IN] Log file that stores error information.
 * @param  lineNo    [IN] Line number of the erroneous line.
 * @param  errorNo   [IN] Error code.
 * @param  paraLen   [IN] Length of the input parameter pPara.
 * @param  para      [IN] User label of the error.
 *
 * @retval None.
 * @par Dependency:
 * <ul><li>los_err.h: the header file that contains the API declaration.</li></ul>
 * @see None.
 *
 * C名称：LOS_ERRORHANDLE_FUNC
 * void *para暂用字节切片代替
 */
type LosErrorHandleFunc = fn(fileName: &str, lineNo: u32, errorNo: u32, paraLen: u32, para: &[u8]);

/**
 * @ingroup los_err
 * Error handling function structure.
 */
struct UserErrFunc {
  pfnHook: Option<LosErrorHandleFunc>,
}


/*LITE_OS_SEC_BSS*/ static mut g_userErrFunc: UserErrFunc = UserErrFunc { pfnHook: None };

/**
 * @ingroup los_err
 * @brief Error handling function.
 *
 * @par Description:
 * This API is used to perform different operations according to error types.
 * @attention
 * <ul>
 * <li>None</li>
 * </ul>
 *
 * @param  fileName  [IN] Log file that stores error information.
 * @param  lineNo    [IN] Line number of the erroneous line which should not be OS_ERR_MAGIC_WORD.
 * @param  errorNo   [IN] Error code.
 * @param  paraLen   [IN] Length of the input parameter pPara.
 * @param  para      [IN] User label of the error.
 *
 * @retval LOS_OK The error is successfully processed.
 * @par Dependency:
 * <ul><li>los_err.h: the header file that contains the API declaration.</li></ul>
 * @see None
 */
/*LITE_OS_SEC_TEXT_INIT*/ pub fn LOS_ErrHandle(fileName: &str, lineNo: u32, errorNo: u32, paraLen: u32, para: &[u8]) -> u32 {
  unsafe {
    if let Some(f) = g_userErrFunc.pfnHook {
      f(fileName, lineNo, errorNo, paraLen, para);
    }
  }
  0
}

/// 各模块的代码表示
///
/// C名称：LOS_MODULE_ID
pub enum LosModuleId {
  LOS_MOD_SYS              = 0x0,
  LOS_MOD_MEM              = 0x1,
  LOS_MOD_TSK              = 0x2,
  LOS_MOD_SWTMR            = 0x3,
  LOS_MOD_TICK             = 0x4,
  LOS_MOD_MSG              = 0x5,
  LOS_MOD_QUE              = 0x6,
  LOS_MOD_SEM              = 0x7,
  LOS_MOD_MBOX             = 0x8,
  LOS_MOD_HWI              = 0x9,
  LOS_MOD_HWWDG            = 0xa,
  LOS_MOD_CACHE            = 0xb,
  LOS_MOD_HWTMR            = 0xc,
  LOS_MOD_MMU              = 0xd,

  LOS_MOD_LOG              = 0xe,
  LOS_MOD_ERR              = 0xf,

  LOS_MOD_EXC              = 0x10,
  LOS_MOD_CSTK             = 0x11,

  LOS_MOD_MPU              = 0x12,
  LOS_MOD_NMHWI            = 0x13,
  LOS_MOD_TRACE            = 0x14,
  LOS_MOD_IPC              = 0x18,
  LOS_MOD_TIMER            = 0x1a,
  LOS_MOD_EVENT            = 0x1c,
  LOS_MOD_MUX              = 0x1d,
  LOS_MOD_CPUP             = 0x1e,
  LOS_MOD_HOOK             = 0x1f,
  LOS_MOD_PM               = 0x20,
  LOS_MOD_SHELL            = 0x31,
  LOS_MOD_BUTT
}

/**
 * @ingroup los_err
 * Define the error magic word.
 */
const OS_ERR_MAGIC_WORD: u32 = 0xa1b2c3f8;

/**
 * @ingroup los_err
 * @brief Error handling macro capable of returning error codes.
 *
 * @par Description:
 * This API is used to call the error handling function by using an error code and return the same error code.
 * @attention
 * <ul>
 * <li>None.</li>
 * </ul>
 *
 * @param  errNo   [IN] Error code.
 *
 * @retval errNo
 * @par Dependency:
 * <ul><li>los_err_pri.h: the header file that contains the API declaration.</li></ul>
 * @see None.
 *
 * 参数只接受变量名
 * C名称：OS_RETURN_ERROR
 */
#[macro_export]
macro_rules! os_return_error {
  ($errno: ident) => {
    {
      LOS_ErrHandle(&"os_unspecific_file", OS_ERR_MAGIC_WORD, $errno, 0, &[]);
      return $errno;
    }
  };
}

/**
 * @ingroup los_err
 * @brief Error handling macro capable of returning error codes.
 *
 * @par Description:
 * This API is used to call the error handling function by using an error code and the line number of the erroneous line,
   and return the same error code.
 * @attention
 * <ul>
 * <li>None.</li>
 * </ul>
 *
 * @param  errLine   [IN] Line number of the erroneous line.
 * @param  errNo     [IN] Error code.
 *
 * @retval errNo
 * @par Dependency:
 * <ul><li>los_err_pri.h: the header file that contains the API declaration.</li></ul>
 * @see None.
 *
 * 参数只接受变量名
 * C名称：OS_RETURN_ERROR_P2
 */
#[macro_export]
macro_rules! os_return_error_p2 {
  ($errLine: ident, $errno: ident) => {
    {
      LOS_ErrHandle(&"os_unspecific_file", $errLine, $errno, 0, &[]);
      return $errno;
    }
  };
}

// 测试方法：将本文件拷贝为常规crate的src/lib.rs，cargo test
#[cfg(test)]
mod tests {
  use super::*;

  #[test]
  fn test_fatal() {
    const ERR_NO: u32 = los_errno_os_fatal!(LosModuleId::LOS_MOD_MBOX, 11);
    assert_eq!(ERR_NO, 0x0300080b);
  }

  #[test]
  fn test_error() {
    const ERR_NO: u32 = los_errno_os_error!(LosModuleId::LOS_MOD_IPC, 5);
    assert_eq!(ERR_NO, 0x02001805);
  }

  #[test]
  fn test_warn() {
    const ERR_NO: u32 = los_errno_os_warn!(LosModuleId::LOS_MOD_CPUP, 16);
    assert_eq!(ERR_NO, 0x01001e10);
  }

  #[test]
  fn test_normal() {
    const ERR_NO: u32 = los_errno_os_normal!(LosModuleId::LOS_MOD_SHELL, 6);
    assert_eq!(ERR_NO, 0x00003106);
  }

  fn default_err_handle(fileName: &str, lineNo: u32, errorNo: u32, paraLen: u32, para: &[u8]) {
    println!("file:{}, line:{}, err:{}, para len:{}", fileName, lineNo, errorNo, paraLen);
    for c in para {
      print!("{:02x} ", c);
    }
  }

  #[test]
  fn test_handle() {
    println!("handle is null, call has no effect");
    let err = LOS_ErrHandle(&"a.txt", 3, 4, 5, &b"abcde"[..]);
    assert_eq!(err, 0);

    println!("set handler and try again");
    unsafe {
      g_userErrFunc = UserErrFunc { pfnHook: Some(default_err_handle) };
    }
    let err = LOS_ErrHandle(&"a.txt", 3, 4, 5, &b"abcde"[..]);
    assert_eq!(err, 0);
    // 再试一次看g_userErrFunc中的函数指针是否还存在
    LOS_ErrHandle(&"a.txt", 3, 4, 5, &b"abcde"[..]);
    unsafe {
      if let None = g_userErrFunc.pfnHook {
        panic!("function pointer was gone");
      }
    }
  }

  fn err_func(u: u32) -> u32 {
    os_return_error!(u)
  }

  #[test]
  fn test_return_err() {
    assert_eq!(err_func(13), 13);
  }

  fn err_func_p2(l: u32) -> u32 {
    let err = 9u32;
    os_return_error_p2!(l, err)
  }

  #[test]
  fn test_return_err_p2() {
    assert_eq!(err_func_p2(20), 9);
  }
}
